////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __WN_SEMAPHORE_INL__
#define __WN_SEMAPHORE_INL__

#include "WNCore/inc/WNBase.h"
#include "WNCore/inc/WNAssert.h"

#ifdef _WN_WINDOWS
    #include <windows.h>
    #include <limits.h>
#elif defined _WN_LINUX
    #include <semaphore.h>
#endif

__WN_INLINE WN_VOID WNSemaphoreInitialize(WN_SEMAPHORE_T* _semaphore) {
    WN_RELEASE_ASSERT(_semaphore != WN_NULL);

    #ifdef _WN_WINDOWS
        *(_semaphore) = CreateSemaphore(WN_NULL, 0, LONG_MAX, WN_NULL);

        WN_RELEASE_ASSERT(*(_semaphore) != WN_NULL);
    #elif defined _WN_LINUX
        WN_RELEASE_ASSERT(sem_init(_semaphore, 0, 0) == 0);
    #endif
}

__WN_INLINE WN_VOID WNSemaphoreDestroy(WN_SEMAPHORE_T* _semaphore) {
    WN_RELEASE_ASSERT(_semaphore != WN_NULL);

    #ifdef _WN_WINDOWS
        WN_RELEASE_ASSERT(CloseHandle(*(_semaphore)) == 0);
    #elif defined _WN_LINUX
        WN_RELEASE_ASSERT(sem_destroy(_semaphore) == 0);
    #endif
}

__WN_INLINE WN_VOID WNSemaphoreWait(WN_SEMAPHORE_T* _semaphore) {
    WN_RELEASE_ASSERT(_semaphore != WN_NULL);

    #ifdef _WN_WINDOWS
        WN_RELEASE_ASSERT(WaitForSingleObject(*(_semaphore), INFINITE) != WAIT_OBJECT_0);
    #elif defined _WN_LINUX
        WN_RELEASE_ASSERT(sem_wait(_semaphore) == 0);
    #endif
}

__WN_INLINE WN_VOID WNSemaphorePost(WN_SEMAPHORE_T* _semaphore) {
    WN_RELEASE_ASSERT(_semaphore != WN_NULL);

    #ifdef _WN_WINDOWS
        WN_RELEASE_ASSERT(ReleaseSemaphore(*(_semaphore), 1, WN_NULL) != 0);
    #elif defined _WN_LINUX
        WN_RELEASE_ASSERT(sem_post(_semaphore) == 0);
    #endif
}

#ifdef _WN_CPLUSPLUS
    __WN_INLINE WNSemaphore::WNSemaphore() {
        WNSemaphoreInitialize(&mSemaphore);
    }

    __WN_INLINE WNSemaphore::~WNSemaphore() {
        WNSemaphoreDestroy(&mSemaphore);
    }

    __WN_INLINE WN_VOID WNSemaphore::Wait() {
        WNSemaphoreWait(&mSemaphore);
    }

    __WN_INLINE WN_VOID WNSemaphore::Post() {
        WNSemaphorePost(&mSemaphore);
    }

    __WN_INLINE WNSemaphoreScopeLock::WNSemaphoreScopeLock(WN_SEMAPHORE_T* _semaphore) {
        WN_RELEASE_ASSERT(_semaphore != WN_NULL);

        mSemaphore = _semaphore;

        WNSemaphoreWait(mSemaphore);
    }

    __WN_INLINE WNSemaphoreScopeLock::WNSemaphoreScopeLock(WNSemaphore& _semaphore) {
        mSemaphore = &(_semaphore.mSemaphore);

        WNSemaphoreWait(mSemaphore);
    }

    __WN_INLINE WNSemaphoreScopeLock::~WNSemaphoreScopeLock() {
        WNSemaphorePost(mSemaphore);
    }
#endif

#endif // __WN_SEMAPHORE_INL__
